<script lang="ts" setup>
import type { MetaType, PlanLimitExceededDetailsType, Roles, WorkspaceUserRoles } from 'nocodb-sdk'
import { OrderedProjectRoles, OrgUserRoles, ProjectRoles, WorkspaceRolesToProjectRoles } from 'nocodb-sdk'

const props = defineProps<{
  baseId?: string
}>()

const router = useRouter()

const { isPrivateBase, base } = storeToRefs(useBase())

const basesStore = useBases()
const { getBaseUsers, createProjectUser, updateProjectUser, removeProjectUser } = basesStore
const { activeProjectId, bases, basesUser } = storeToRefs(basesStore)

const { orgRoles, baseRoles, loadRoles, isUIAllowed } = useRoles()

const { sorts, sortDirection, loadSorts, handleGetSortedData, saveOrUpdate: saveOrUpdateUserSort } = useUserSorts('Project')

const isSuper = computed(() => orgRoles.value?.[OrgUserRoles.SUPER_ADMIN])

const orgStore = useOrg()
const { orgId, org } = storeToRefs(orgStore)

const isAdminPanel = inject(IsAdminPanelInj, ref(false))

const { $api, $eventBus } = useNuxtApp()

const { t } = useI18n()

const { projectPageTab } = storeToRefs(useConfigStore())

const { isPaymentEnabled, showUserPlanLimitExceededModal } = useEeConfig()

const currentBase = computedAsync(async () => {
  let base
  if (props.baseId) {
    await loadRoles(props.baseId)
    base = bases.value.get(props.baseId)
    if (!base) {
      base = await $api.base.read(props.baseId!)
    }
  } else {
    base = bases.value.get(activeProjectId.value)
  }
  return base
})

const isInviteModalVisible = ref(false)

interface Collaborators {
  id: string
  email: string
  main_roles: OrgUserRoles
  roles: ProjectRoles
  base_roles: Roles
  workspace_roles: WorkspaceUserRoles
  created_at: string
  display_name: string | null
  meta: MetaType
}

const collaborators = ref<Collaborators[]>([])
const totalCollaborators = ref(0)
const userSearchText = ref('')

const isLoading = ref(false)
const accessibleRoles = ref<(typeof ProjectRoles)[keyof typeof ProjectRoles][]>([])

const filteredCollaborators = computed(() =>
  collaborators.value.filter(
    (collab) =>
      collab.display_name?.toLowerCase()?.includes(userSearchText.value.toLowerCase()) ||
      collab.email.toLowerCase().includes(userSearchText.value.toLowerCase()),
  ),
)

const sortedCollaborators = computed(() => {
  return handleGetSortedData(filteredCollaborators.value, sorts.value)
})

const loadCollaborators = async () => {
  try {
    if (!currentBase.value) return
    const { users, totalRows } = await getBaseUsers({
      baseId: currentBase.value.id!,
      ...(!userSearchText.value ? {} : ({ searchText: userSearchText.value } as any)),
      force: true,
    })

    totalCollaborators.value = totalRows
    collaborators.value = [
      ...users
        .filter((u: any) => !u?.deleted)
        .map((user: any) => ({
          ...user,
          base_roles: user.roles,
          roles:
            user.roles ??
            (user.workspace_roles
              ? WorkspaceRolesToProjectRoles[user.workspace_roles as WorkspaceUserRoles] ?? ProjectRoles.NO_ACCESS
              : ProjectRoles.NO_ACCESS),
        })),
    ]
  } catch (e: any) {
    message.error(await extractSdkResponseErrorMsg(e))
  }
}

const isOwnerOrCreator = computed(() => {
  return baseRoles.value?.[ProjectRoles.OWNER] || baseRoles.value?.[ProjectRoles.CREATOR]
})

const updateCollaborator = async (collab: any, roles: ProjectRoles) => {
  const currentCollaborator = collaborators.value.find((coll) => coll.id === collab.id)!

  try {
    if (!roles || (roles === ProjectRoles.NO_ACCESS && !isEeUI)) {
      await removeProjectUser(currentBase.value.id!, currentCollaborator as unknown as User)
      if (
        currentCollaborator.workspace_roles &&
        WorkspaceRolesToProjectRoles[currentCollaborator.workspace_roles as WorkspaceUserRoles] === roles &&
        isEeUI
      ) {
        currentCollaborator.roles = WorkspaceRolesToProjectRoles[currentCollaborator.workspace_roles as WorkspaceUserRoles]
      } else {
        currentCollaborator.roles = ProjectRoles.NO_ACCESS
      }
      currentCollaborator.base_roles = null
    } else if (currentCollaborator.base_roles) {
      currentCollaborator.roles = roles
      await updateProjectUser(currentBase.value.id!, currentCollaborator as unknown as User)
    } else {
      currentCollaborator.roles = roles
      currentCollaborator.base_roles = roles
      await createProjectUser(currentBase.value.id!, currentCollaborator as unknown as User)
    }

    let currentBaseUsers = basesUser.value.get(currentBase.value.id)

    if (currentBaseUsers?.length) {
      currentBaseUsers = currentBaseUsers.map((user) => {
        if (user.id === currentCollaborator.id) {
          user.roles = currentCollaborator.roles as any
        }
        return user
      })

      basesUser.value.set(currentBase.value.id, currentBaseUsers)
    }
  } catch (e: any) {
    const errorInfo = await extractSdkResponseErrorMsgv2(e)

    if (isPaymentEnabled.value && errorInfo.error === NcErrorType.PLAN_LIMIT_EXCEEDED) {
      const details = errorInfo.details as PlanLimitExceededDetailsType

      showUserPlanLimitExceededModal({
        details,
        role: roles,
      })
    } else {
      message.error(errorInfo.message)
    }
  } finally {
    loadCollaborators()
  }
}
const showOverlay = computed(() => {
  return base.value?.default_role && !baseRoles.value?.[ProjectRoles.OWNER]
})

onMounted(async () => {
  if (showOverlay.value) return
  isLoading.value = true
  try {
    await loadCollaborators()
    const currentRoleIndex = OrderedProjectRoles.findIndex(
      (role) => baseRoles.value && Object.keys(baseRoles.value).includes(role),
    )
    if (isSuper.value) {
      accessibleRoles.value = OrderedProjectRoles.slice(0)
    } else if (currentRoleIndex !== -1) {
      accessibleRoles.value = OrderedProjectRoles.slice(currentRoleIndex)
    }
    loadSorts()
  } catch (e: any) {
    message.error(await extractSdkResponseErrorMsg(e))
  } finally {
    isLoading.value = false
  }
})

watch(baseRoles, (br) => {
  const currentRoleIndex = OrderedProjectRoles.findIndex((role) => br && Object.keys(br).includes(role))
  accessibleRoles.value = OrderedProjectRoles.slice(currentRoleIndex)
})

const selected = reactive<{
  [key: string]: boolean
}>({})

const toggleSelectAll = (value: boolean) => {
  filteredCollaborators.value.forEach((_) => {
    selected[_.id] = value
  })
}

// const isSomeSelected = computed(() => Object.values(selected).some((v) => v))

const selectAll = computed({
  get: () =>
    Object.values(selected).every((v) => v) &&
    Object.keys(selected).length > 0 &&
    Object.values(selected).length === filteredCollaborators.value.length,
  set: (value) => {
    toggleSelectAll(value)
  },
})
watch(isInviteModalVisible, () => {
  if (!isInviteModalVisible.value) {
    loadCollaborators()
  }
})

watch(currentBase, () => {
  loadCollaborators()
})

const orderBy = computed<Record<string, SordDirectionType>>({
  get: () => {
    return sortDirection.value
  },
  set: (value: Record<string, SordDirectionType>) => {
    // Check if value is an empty object
    if (Object.keys(value).length === 0) {
      saveOrUpdateUserSort({})
      return
    }

    const [field, direction] = Object.entries(value)[0]

    saveOrUpdateUserSort({
      field,
      direction,
    })
  },
})

const columns = [
  // // Enable this select row column once we introduce bulk action
  // {
  //   key: 'select',
  //   title: '',
  //   width: 70,
  //   minWidth: 70,
  // },
  {
    key: 'email',
    title: t('labels.members'),
    minWidth: 220,
    dataIndex: 'email',
    showOrderBy: true,
  },
  {
    key: 'role',
    title: t('general.role'),
    basis: '30%',
    minWidth: 272,
    dataIndex: 'roles',
    showOrderBy: true,
  },
  {
    key: 'created_at',
    title: t('title.dateJoined'),
    basis: '20%',
    minWidth: 200,
  },
  {
    key: 'action',
    title: t('labels.actions'),
    width: 110,
    minWidth: 110,
    justify: 'justify-end',
  },
] as NcTableColumnProps[]

const customRow = (record: Record<string, any>) => ({
  class: `${selected[record.id] ? 'selected' : ''} user-row`,
})

const isOnlyOneOwner = computed(() => {
  return collaborators.value?.filter((collab) => collab.roles === ProjectRoles.OWNER).length === 1
})

const isDeleteOrUpdateAllowed = (user) => {
  return !(isOnlyOneOwner.value && user.roles === ProjectRoles.OWNER)
}

const goToBaseSettings = () => {
  router.push({
    query: {
      ...router.currentRoute.value.query,
      page: 'base-settings',
      tab: 'baseType',
    },
  })
}

watch(projectPageTab, () => {
  if (!userSearchText.value) return

  userSearchText.value = ''
})

const evtListener = (event: string, data: any) => {
  if (data.baseId !== currentBase.value?.id) return

  if (event === 'base_user_update') {
    // Handle the event
    loadCollaborators()
  }
}

onMounted(() => {
  loadSorts()
  $eventBus.realtimeBaseUserEventBus.on(evtListener)
})

onBeforeUnmount(() => {
  $eventBus.realtimeBaseUserEventBus.off(evtListener)
})
</script>

<template>
  <div
    class="nc-collaborator-table-container nc-access-settings-view flex flex-col relative"
    :class="{
      'nc-admin-panel': isAdminPanel,
    }"
  >
    <ProjectPrivateOverlay v-if="showOverlay" />
    <template v-else>
      <div v-if="isAdminPanel">
        <div class="nc-breadcrumb px-2">
          <div class="nc-breadcrumb-item">
            {{ org.title }}
          </div>
          <GeneralIcon icon="ncSlash1" class="nc-breadcrumb-divider" />
          <NuxtLink
            :href="`/admin/${orgId}/bases`"
            class="!hover:(text-gray-800 underline-gray-600) flex items-center !text-gray-700 !underline-transparent max-w-1/4"
          >
            <div class="nc-breadcrumb-item">
              {{ $t('objects.projects') }}
            </div>
          </NuxtLink>
          <GeneralIcon icon="ncSlash1" class="nc-breadcrumb-divider" />
          <div class="nc-breadcrumb-item active truncate capitalize">
            {{ currentBase?.title }}
          </div>
        </div>
        <NcPageHeader>
          <template #icon>
            <div class="nc-page-header-icon flex justify-center items-center h-5 w-5">
              <GeneralBaseIconColorPicker readonly />
            </div>
          </template>
          <template #title>
            <span data-rec="true" class="capitalize">
              {{ currentBase?.title }}
            </span>
          </template>
        </NcPageHeader>
      </div>

      <div class="nc-content-max-w h-full flex flex-col items-center gap-6 px-4 md:px-6 pt-6">
        <NcAlert v-if="isEeUI && isPrivateBase" type="info" :message="$t('title.privateBase')" class="bg-nc-bg-gray-extralight">
          <template #icon>
            <GeneralIcon icon="ncUser" class="w-6 h-6 text-nc-content-gray-subtle" />
          </template>
          <template #description>
            {{ $t('title.privateBaseAlertDescription') }}
          </template>

          <template v-if="isUIAllowed('manageBaseType')" #action>
            <NcButton
              type="secondary"
              size="small"
              class="!mt-[-4px]"
              inner-class="!gap-1.5"
              icon-position="right"
              @click="goToBaseSettings"
            >
              <template #icon>
                <GeneralIcon icon="ncArrowUpRight" class="w-4 h-4" />
              </template>
              {{ $t('activity.goToBaseSettings') }}
            </NcButton>
          </template>
        </NcAlert>
        <div v-if="!isAdminPanel" class="w-full flex justify-between items-center max-w-full gap-3">
          <a-input
            v-model:value="userSearchText"
            :placeholder="$t('title.searchMembers')"
            :disabled="isLoading"
            allow-clear
            class="nc-input-border-on-value !max-w-90 !h-8 !px-3 !py-1 !rounded-lg"
          >
            <template #prefix>
              <GeneralIcon icon="search" class="mr-2 h-4 w-4 text-gray-500 group-hover:text-black" />
            </template>
          </a-input>

          <NcButton :disabled="isLoading" size="small" class="flex-none" @click="isInviteModalVisible = true">
            <div class="flex items-center gap-1">
              <component :is="iconMap.plus" class="w-4 h-4" />
              {{ $t('activity.addMembers') }}
            </div>
          </NcButton>
        </div>

        <NcTable
          v-model:order-by="orderBy"
          :is-data-loading="isLoading"
          :columns="columns"
          :data="sortedCollaborators"
          :bordered="false"
          :custom-row="customRow"
          class="flex-1 nc-collaborators-list max-w-full"
          body-row-class-name="!cursor-default"
          :pagination="true"
          :pagination-offset="25"
        >
          <template #emptyText>
            <a-empty :description="$t('title.noMembersFound')" />
          </template>

          <template #headerCell="{ column }">
            <template v-if="column.key === 'select'">
              <NcCheckbox v-model:checked="selectAll" :disabled="!sortedCollaborators.length" />
            </template>
            <template v-else>
              {{ column.title }}
            </template>
          </template>

          <template #bodyCell="{ column, record }">
            <template v-if="column.key === 'select'">
              <NcCheckbox v-model:checked="selected[record.id]" />
            </template>

            <div v-if="column.key === 'email'" class="w-full flex gap-3 items-center users-email-grid">
              <GeneralUserIcon size="base" :user="record" class="flex-none" />
              <div class="flex flex-col flex-1 max-w-[calc(100%_-_44px)]">
                <div class="flex gap-3">
                  <NcTooltip class="truncate max-w-full text-gray-800 capitalize font-semibold" show-on-truncate-only>
                    <template #title>
                      {{ record.display_name || record.email.slice(0, record.email.indexOf('@')) }}
                    </template>
                    {{ record.display_name || record.email.slice(0, record.email.indexOf('@')) }}
                  </NcTooltip>
                </div>
                <NcTooltip class="truncate max-w-full text-xs text-gray-600" show-on-truncate-only>
                  <template #title>
                    {{ record.email }}
                  </template>
                  {{ record.email }}
                </NcTooltip>
              </div>
            </div>
            <div v-if="column.key === 'role'">
              <template v-if="isDeleteOrUpdateAllowed(record) && isOwnerOrCreator && accessibleRoles.includes(record.roles)">
                <RolesSelectorV2
                  :role="record.roles"
                  :roles="accessibleRoles"
                  :inherit="
                    isEeUI && !record.base_roles && record.workspace_roles && WorkspaceRolesToProjectRoles[record.workspace_roles]
                      ? WorkspaceRolesToProjectRoles[record.workspace_roles]
                      : null
                  "
                  show-inherit
                  :on-role-change="(role) => updateCollaborator(record, role as ProjectRoles)"
                />
              </template>
              <template v-else>
                <RolesBadge :border="false" :role="record.roles" />
              </template>
            </div>
            <div v-if="column.key === 'created_at'">
              <NcTooltip class="max-w-full">
                <template #title>
                  {{ parseStringDateTime(record.created_at) }}
                </template>
                <span>
                  {{ timeAgo(record.created_at) }}
                </span>
              </NcTooltip>
            </div>
            <div v-if="column.key === 'action'">
              <NcDropdown placement="bottomRight">
                <NcButton size="small" type="secondary">
                  <component :is="iconMap.ncMoreVertical" />
                </NcButton>
                <template #overlay>
                  <NcMenu variant="small">
                    <NcMenuItemCopyId
                      :id="record.id"
                      :tooltip="$t('labels.clickToCopyUserID')"
                      :label="
                        $t('labels.userIdColon', {
                          userId: record.id,
                        })
                      "
                    />
                  </NcMenu>
                </template>
              </NcDropdown>
            </div>
          </template>
        </NcTable>
      </div>

      <LazyDlgInviteDlg
        v-model:model-value="isInviteModalVisible"
        :base-id="currentBase?.id"
        :users="collaborators"
        type="base"
      /> </template
    ><x></x>
  </div>
</template>

<style scoped lang="scss">
.color-band {
  @apply w-6 h-6 left-0 top-2.5 rounded-full flex justify-center uppercase text-white font-weight-bold text-xs items-center;
}

:deep(.nc-collaborator-role-select .ant-select-selector) {
  @apply !rounded;
}

.nc-page-header-icon {
  :deep(svg) {
    @apply h-4.5 w-4.5;
  }
}

.nc-collaborator-table-container {
  &:not(.nc-admin-panel) {
    @apply h-[calc(100vh-var(--topbar-height)-44px)];

    @supports (height: 100dvh) {
      @apply h-[calc(100dvh-var(--topbar-height)-44px)];
    }
  }
}
</style>
